﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a catalog item rating.
    /// </summary>
    public class CreateCatalogItemRating : CreateRating
        <CatalogItemRatingEntity, CatalogItemCommentEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) => ctx.Account(uow).EntityID == AccountID);
                yield return new SecurityPolicy()
                    .Rule((ctx, uow) => AccountID == 0);
            }
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogItemRatingFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            var item = uow.Execute(new GetCatalogItem
            {
                ThrowsException = true,
                EntityID = HolderID
            }).Result;
            if(!item.Flags.HasFlag(CatalogItemFlags.RatingAllowed))
                return Err.Of(this).NotAllowed();
            if(!uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Catalog").Of<CatalogItemRatingEntity>().Name("Allowed")
            }).ResultAs<Boolean>())
            {
                return Err.Of(this).NotAllowed();
            }

            var account = AccountID != 0
                ? uow.Execute(new GetAccount
                {
                    ThrowsException = true,
                    EntityID = AccountID
                }).Result
                : null;
            if(account == null)
            {
                if(!item.Flags.HasFlag(CatalogItemFlags.AnonRatingAllowed))
                    return Err.Of(this).NotAllowed().Reason("Anon");
                if(!uow.Execute(new GetConfigParamValueByKey
                {
                    Key = Par.In("Catalog").Of<CatalogItemRatingEntity>().Name("AnonAllowed")
                }).ResultAs<Boolean>())
                {
                    return Err.Of(this).NotAllowed().Reason("Anon");
                }
            }

            byte valueMin = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Catalog").Of<CatalogItemRatingEntity>().Name("Min")
            }).ResultAs<Byte>();
            if(Value < valueMin)
                return Err.Of<CatalogItemRatingEntity>().NotValid("Value");
            byte valueMax = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Catalog").Of<CatalogItemRatingEntity>().Name("Max")
            }).ResultAs<Byte>();
            if(Value > valueMax)
                return Err.Of<CatalogItemRatingEntity>().NotValid("Value");

            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out CatalogItemRatingEntity result)
        {
            base.Work(uow, out result);

            uow.Execute(new UpdateCatalogItemStats
            {
                EntityID = result.HolderID
            });
            foreach(var i in uow.Execute(new GetCatalogItem
            {
                ThrowsException = true,
                EntityID = result.HolderID
            }).Result.Supplier.Managers)
            {
                try
                {
                    uow.Execute(new EnqueueCatalogItemRatingNotification
                    {
                        RatingID = result.EntityID,
                        RecipientID = i.Account.EntityID
                    });
                }
                catch(Exception ex)
                {
                    uow.Execute(new CreateLogRecordFromException
                    {
                        Ex = ex
                    });
                }
            }
        }

        protected override void Construct(IUnitOfWork uow, CatalogItemRatingEntity entity)
        {
            base.Construct(uow, entity);

            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the catalog item rating.
    /// </summary>
    public class UpdateCatalogItemRating : UpdateRating<CatalogItemRatingEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
            }
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogItemRatingFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            byte valueMin = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Catalog").Of<CatalogItemRatingEntity>().Name("Min")
            }).ResultAs<Byte>();
            if(Value < valueMin)
                return Err.Of<CatalogItemRatingEntity>().NotValid("Value");
            byte valueMax = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Catalog").Of<CatalogItemRatingEntity>().Name("Max")
            }).ResultAs<Byte>();
            if(Value > valueMax)
                return Err.Of<CatalogItemRatingEntity>().NotValid("Value");
            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out CatalogItemRatingEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateCatalogItemStats
            {
                EntityID = result.HolderID
            });
        }

        protected override void Revise(IUnitOfWork uow, CatalogItemRatingEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the catalog item rating.
    /// </summary>
    public class DeleteCatalogItemRating : DeleteRating<CatalogItemRatingEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow)
        {
            int holderID = uow.Execute(new GetCatalogItemRating
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result.HolderID;

            base.Work(uow);

            uow.Execute(new UpdateCatalogItemStats
            {
                EntityID = holderID
            });
        }

        #endregion
    }

    /// <summary>
    /// Gets the catalog item rating.
    /// </summary>
    public class GetCatalogItemRating : GetRating<CatalogItemRatingEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over catalog item rating collection.
    /// </summary>
    public class SearchCatalogItemRatings : SearchRatings<CatalogItemRatingEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchCatalogItemRatings()
        {
            SortDir = SortDirection.Ascending;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CatalogItemRatingFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<CatalogItemRatingEntity> Build(IUnitOfWork uow, IQueryable<CatalogItemRatingEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => x.FlagsValue, (int)FlagMask);
            query = query.OrderBy(x => x.CreatedOn, SortDir);
            return query;
        }

        #endregion
    }

    #endregion

    #region __Notification__

    /// <summary>
    /// Enqueues a catalog item rating notification message.
    /// </summary>
    /// <remarks>
    /// Message template code: NCATITMRTG
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid] [T:Recipient.Email],  [T:Recipient.Username], 
    /// [T:CatalogItemRating.Item.EntityID],  [T:CatalogItemRating.Item.RatingAverage], [T:CatalogItemRating.Item.RatingCount],
    /// [T:CatalogItemRating.Item.Name], [T:CatalogItemRating.Item.ShortDescription], [T:CatalogItemRating.Account.EntityID], 
    /// [T:CatalogItemRating.Account.Username], [T:CatalogItemRating.Account.Email], [T:CatalogItemRating.Account.Guid], 
    /// [T:CatalogItemRating.EntityID], [T:CatalogItemRating.Value], [T:CatalogItemRating.CommentCount], 
    /// [T:CatalogItemRating.CreatedOn]
    /// </remarks>
    public class EnqueueCatalogItemRatingNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the rating identifier.
        /// </summary>
        public int RatingID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient account identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var rating = uow.Execute(new GetCatalogItemRating
            {
                ThrowsException = true,
                EntityID = RatingID
            }).Result;
            var recipient = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var usrLocale = recipient.LocalizationProfile;
            int utcOffset = usrLocale.TimeZone.UtcOffset;
            string dtFmt = usrLocale.Country.DateTimeFormat;
            var item = uow.Execute(new GetCatalogItem
            {
                ThrowsException = true,
                EntityID = rating.HolderID
            }).Result;
            var t_item = item.Translation(usrLocale.Language.EntityID);

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "NCATITMRTG",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"CatalogItemRating.Item.EntityID", item.EntityID.ToString()},
                    {"CatalogItemRating.Item.RatingAverage", item.RatingAverage.ToString()},
                    {"CatalogItemRating.Item.RatingCount", item.RatingCount.ToString()},
                    {"CatalogItemRating.Item.CommentCount", item.CommentCount.ToString()},
                    {"CatalogItemRating.Item.Name", t_item != null 
                        ? t_item.Name 
                        : String.Empty},
                    {"CatalogItemRating.Item.ShortDescription", t_item != null 
                        ? t_item.ShortDescription 
                        : String.Empty},
                    {"CatalogItemRating.Account.EntityID", rating.Account != null 
                        ? rating.Account.EntityID.ToString() 
                        : String.Empty},
                    {"CatalogItemRating.Account.Username", rating.Account != null 
                        ? rating.Account.Username 
                        : String.Empty},
                    {"CatalogItemRating.Account.Email", rating.Account != null 
                        ? rating.Account.Email 
                        : String.Empty},
                    {"CatalogItemRating.Account.Guid", rating.Account != null 
                        ? rating.Account.Guid.ToString() 
                        : String.Empty},
                    {"CatalogItemRating.EntityID", rating.EntityID.ToString()},
                    {"CatalogItemRating.Value", rating.Value.ToString()},
                    {"CatalogItemRating.CreatedOn", rating.CreatedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    #endregion
}
